Bring in the KML writer from Google Earth 5.0. Set the extents of the
authorrobertl <robertl>
Wed, 4 Feb 2009 23:40:47 +0000 (23:40 +0000)
committerrobertl <robertl>
Wed, 4 Feb 2009 23:40:47 +0000 (23:40 +0000)
 time sliders automatically.

defs.h
kml.c
reference/earth-expertgps.kml
reference/earth-gc.kml

diff --git a/defs.h b/defs.h
index 984c7c6f40c9afaf5bb9405253310815134dc4a5..4c8ea1bebbe7a0c4105a72d8425651d8ceb3eb7c 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -542,6 +542,8 @@ void waypt_init_bounds(bounds *bounds);
 int waypt_bounds_valid(bounds *bounds);
 void waypt_add_to_bounds(bounds *bounds, const waypoint *waypointp);
 void waypt_compute_bounds(bounds *);
+double gcgeodist(const double lat1, const double lon1,
+                 const double lat2, const double lon2);
 void waypt_flush(queue *);
 void waypt_flush_all(void);
 unsigned int waypt_count(void);
diff --git a/kml.c b/kml.c
index 0de45c600cc46203abd080987db64e331233704c..b3847c2637302c2b45468de455e1f94043c2d895 100644 (file)
--- a/kml.c
+++ b/kml.c
@@ -1,7 +1,8 @@
-/* 
+/*
        Support for Google Earth & Keyhole "kml" format.
 
-       Copyright (C) 2005, 2006, 2007 Robert Lipe, robertlipe@usa.net
+       Copyright (C) 2005, 2006, 2007, 2008, 2009  Robert Lipe, 
+           robertlipe@gpsbabel.org
        Updates by Andrew Kirmse, akirmse at google.com
 
        This program is free software; you can redistribute it and/or modify
@@ -76,6 +77,9 @@ static int      point3d_list_len;
 static point3d *point3d_list;
 static int realtime_positioning;
 static int do_indentation = 1;
+static bounds kml_bounds;
+static time_t kml_time_min;
+static time_t kml_time_max;
 
 #define TD(FMT,DATA) kml_write_xml(0, "<tr><td>" FMT " </td></tr>\n", DATA)
 #define TD2(FMT,DATA, DATA2) kml_write_xml(0, "<tr><td>" FMT " </td></tr>\n", DATA, DATA2)
@@ -83,49 +87,40 @@ static int do_indentation = 1;
 //  Icons provided and hosted by Google.  Used with permission.
 #define ICON_BASE "http://earth.google.com/images/kml-icons/"
 
-static const char kml22_hdr[] =  
-       "<kml xmlns=\"http://earth.google.com/kml/2.2\"\n"
-       "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
-//     No "baked in" schemaLocation, per Google recommendation.
-//     "\txsi:schemaLocation=\"http://earth.google.com/kml/2.2 \n"
-//     "\thttp://code.google.com/apis/kml/schema/kml22beta.xsd\">\n";
-       
-static const char kml21_hdr[] =  
-       "<kml xmlns=\"http://earth.google.com/kml/2.1\"\n"
-       "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n";
-//     No "baked in" schemaLocation, per Google recommendation.
-//     "\txsi:schemaLocation=\"http://earth.google.com/kml/2.1 \n"
-//     "\thttp://code.google.com/apis/kml/schema/kml21.xsd\">\n";
+static const char kml22_hdr[] =
+  "<kml xmlns=\"http://www.opengis.net/kml/2.2\"\n"
+  "\txmlns:gx=\"http://www.google.com/kml/ext/2.2\">\n";
+
 
 static
 arglist_t kml_args[] = {
        {"deficon", &opt_deficon, "Default icon name", NULL, ARGTYPE_STRING, ARG_NOMINMAX },
-       {"lines", &opt_export_lines, 
+       {"lines", &opt_export_lines,
          "Export linestrings for tracks and routes",
          "1", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"points", &opt_export_points, 
+       {"points", &opt_export_points,
          "Export placemarks for tracks and routes",
          "1", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"line_width", &opt_line_width, 
+       {"line_width", &opt_line_width,
          "Width of lines, in pixels",
          "6", ARGTYPE_INT, ARG_NOMINMAX },
-       {"line_color", &opt_line_color, 
+       {"line_color", &opt_line_color,
          "Line color, specified in hex AABBGGRR",
          "99ffac59", ARGTYPE_STRING, ARG_NOMINMAX },
-       {"floating", &opt_floating, 
-        "Altitudes are absolute and not clamped to ground", 
+       {"floating", &opt_floating,
+        "Altitudes are absolute and not clamped to ground",
         "0", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"extrude", &opt_extrude, 
-        "Draw extrusion line from trackpoint to ground", 
+       {"extrude", &opt_extrude,
+        "Draw extrusion line from trackpoint to ground",
         "0", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"trackdata", &opt_trackdata, 
-        "Include extended data for trackpoints (default = 1)", 
+       {"trackdata", &opt_trackdata,
+        "Include extended data for trackpoints (default = 1)",
         "1", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"trackdirection", &opt_trackdirection, 
-        "Indicate direction of travel in track icons (default = 0)", 
+       {"trackdirection", &opt_trackdirection,
+        "Indicate direction of travel in track icons (default = 0)",
         "0", ARGTYPE_BOOL, ARG_NOMINMAX },
-       {"units", &opt_units, 
-        "Units used when writing comments ('s'tatute or 'm'etric)", 
+       {"units", &opt_units,
+        "Units used when writing comments ('s'tatute or 'm'etric)",
         "s", ARGTYPE_STRING, ARG_NOMINMAX },
        {"labels", &opt_labels,
         "Display labels on track and routepoints  (default = 1)",
@@ -136,7 +131,7 @@ arglist_t kml_args[] = {
        ARG_TERMINATOR
 };
 
-static 
+static
 struct {
        int freshness;
        const char *icon;
@@ -146,10 +141,10 @@ struct {
  { 0,  ICON_BASE "youarehere-0.png" }, // Green
 };
 
-#define ICON_NOSAT ICON_BASE "youarehere-warning.png";    
+#define ICON_NOSAT ICON_BASE "youarehere-warning.png";
 #define ICON_WPT "http://maps.google.com/mapfiles/kml/pal4/icon61.png"
 #define ICON_TRK ICON_BASE "track-directional/track-none.png"
-#define ICON_RTE ICON_BASE "track-directional/track-none.png"    
+#define ICON_RTE ICON_BASE "track-directional/track-none.png"
 #define ICON_DIR ICON_BASE "track-directional/track-%d.png" // format string where next arg is rotational degrees.
 
 #define MYNAME "kml"
@@ -170,7 +165,7 @@ kml_read(void)
 static xg_callback wpt_s, wpt_e;
 static xg_callback wpt_name, wpt_desc, wpt_coord, wpt_icon, trk_coord, wpt_time;
 
-static 
+static
 xg_tag_mapping kml_map[] = {
        { wpt_s,        cb_start,       "/Placemark" },
        { wpt_e,        cb_end,         "/Placemark" },
@@ -188,7 +183,7 @@ xg_tag_mapping kml_map[] = {
        { NULL,         0,              NULL }
 };
 
-static 
+static
 const char * kml_tags_to_ignore[] = {
        "kml",
        "Document",
@@ -196,8 +191,8 @@ const char * kml_tags_to_ignore[] = {
        NULL,
 };
 
-void wpt_s(const char *args, const char **unused) 
-{ 
+void wpt_s(const char *args, const char **unused)
+{
        wpt_tmp = waypt_new();
        wpt_tmp_queued = 0;
 }
@@ -272,7 +267,7 @@ void trk_coord(const char *args, const char **attrv)
        }
 }
 
-static 
+static
 void
 kml_rd_init(const char *fname)
 {
@@ -298,10 +293,13 @@ static void
 kml_wr_init(const char *fname)
 {
        char u = 's';
+        waypt_init_bounds(&kml_bounds);
+        kml_time_min = 0;
+        kml_time_max = 0;
 
        if (opt_units) {
                u = tolower(opt_units[0]);
-       } 
+       }
 
        switch(u) {
                case 's': fmt_setunits(units_statute); break;
@@ -314,7 +312,7 @@ kml_wr_init(const char *fname)
        ofd = gbfopen(fname, "w", MYNAME);
 }
 
-/* 
+/*
  * The magic here is to try to ensure that posnfilename is atomically
  * updated.
  */
@@ -406,10 +404,10 @@ kml_write_xmle(const char *tag, const char *v)
 }
 
 #define hovertag(h) h ? 'h' : 'n'
-static void kml_write_bitmap_style_(const char *style, const char * bitmap, 
+static void kml_write_bitmap_style_(const char *style, const char * bitmap,
                                    int highlighted, int force_heading)
 {
-       kml_write_xml(0, "<!-- %s %s style -->\n", 
+       kml_write_xml(0, "<!-- %s %s style -->\n",
                highlighted ? "Highlighted" : "Normal", style);
        kml_write_xml(1, "<Style id=\"%s_%c\">\n", style, hovertag(highlighted));
        kml_write_xml(1, "<IconStyle>\n");
@@ -425,7 +423,7 @@ static void kml_write_bitmap_style_(const char *style, const char * bitmap,
        kml_write_xml(-1, "</Icon>\n");
        kml_write_xml(-1, "</IconStyle>\n");
        kml_write_xml(-1, "</Style>\n");
-}  
+}
 
 /* A wrapper for the above function to emit both a highlighted
  * and non-highlighted version of the style to allow the icons
@@ -465,7 +463,7 @@ static void kml_output_timestamp(const waypoint *waypointp)
        if (waypointp->creation_time) {
                xml_fill_in_time(time_string, waypointp->creation_time, waypointp->microseconds, XML_LONG_TIME);
                if (time_string[0]) {
-                       kml_write_xml(0, "<TimeStamp><when>%s</when></TimeStamp>\n", 
+                       kml_write_xml(0, "<TimeStamp><when>%s</when></TimeStamp>\n",
                                time_string);
                }
        }
@@ -474,7 +472,7 @@ static void kml_output_timestamp(const waypoint *waypointp)
 /*
  * Output the track summary.
  */
-static 
+static
 void kml_output_trkdescription(const route_head *header, computed_trkdata *td)
 {
        char *max_alt_units;
@@ -565,7 +563,7 @@ void kml_output_trkdescription(const route_head *header, computed_trkdata *td)
 }
 
 
-static 
+static
 void kml_output_header(const route_head *header, computed_trkdata*td)
 {
        if (!realtime_positioning)  {
@@ -631,13 +629,13 @@ static void kml_output_description(const waypoint *pt)
                TD2("Speed: %.1f %s", spd, spd_units);
        }
        if WAYPT_HAS(pt, course) TD("Heading: %.1f", pt->course);
-       /* This really shouldn't be here, but as of this writing, 
-        * Earth can't edit/display the TimeStamp.  
+       /* This really shouldn't be here, but as of this writing,
+        * Earth can't edit/display the TimeStamp.
         */
        if (pt->creation_time) {
                char time_string[64];
 
-               xml_fill_in_time(time_string, pt->creation_time, 
+               xml_fill_in_time(time_string, pt->creation_time,
                                        pt->microseconds, XML_LONG_TIME);
                 if (time_string[0]) {
                         TD("Time: %s", time_string);
@@ -648,6 +646,18 @@ static void kml_output_description(const waypoint *pt)
        kml_write_xml(-1, "]]></description>\n");
 }
 
+static void kml_recompute_time_bounds(const waypoint *waypointp) {
+  if (waypointp->creation_time && (waypointp->creation_time < kml_time_min)) {
+    kml_time_min = waypointp->creation_time;
+  }
+  if (waypointp->creation_time > kml_time_max) {
+    kml_time_max = waypointp->creation_time;
+    if (kml_time_min == 0) {
+      kml_time_min = waypointp->creation_time;
+    }
+  }
+}
+
 static void kml_output_point(const waypoint *waypointp, kml_point_type pt_type)
 {
   const char *style;
@@ -655,6 +665,9 @@ static void kml_output_point(const waypoint *waypointp, kml_point_type pt_type)
   point3d *pt = &point3d_list[point3d_list_len];
   point3d_list_len++;
 
+  waypt_add_to_bounds(&kml_bounds, waypointp);
+  kml_recompute_time_bounds(waypointp);
+
   switch (pt_type) {
     case kmlpt_track: style = "#track"; break;
     case kmlpt_route: style = "#route"; break;
@@ -693,10 +706,10 @@ static void kml_output_point(const waypoint *waypointp, kml_point_type pt_type)
         } else {
           if (trackdirection && (pt_type == kmlpt_track)) {
                char buf[100];
-               if (waypointp->speed < 1) 
+               if (waypointp->speed < 1)
                        snprintf(buf, sizeof(buf), "%s-none", style);
                else
-                       snprintf(buf, sizeof(buf), "%s-%d", style, 
+                       snprintf(buf, sizeof(buf), "%s-%d", style,
                                (int) (waypointp->course / 22.5 + .5) % 16);
                kml_write_xml(0, "<styleUrl>%s</styleUrl>\n", buf);
          } else {
@@ -728,7 +741,7 @@ static void kml_output_tailer(const route_head *header)
   if (export_points && point3d_list_len > 0) {
     kml_write_xml(-1, "</Folder>\n");
   }
-  
+
   // Add a linestring for this track?
   if (export_lines && point3d_list_len > 0) {
     kml_write_xml(1, "<Placemark>\n");
@@ -755,19 +768,19 @@ static void kml_output_tailer(const route_head *header)
     kml_write_xml(0, "<tessellate>1</tessellate>\n");
     kml_write_xml(1, "<coordinates>\n");
     for (i = 0; i < point3d_list_len; ++i)
-      kml_write_xml(0, "%f,%f,%f\n", 
+      kml_write_xml(0, "%f,%f,%f\n",
               point3d_list[i].longitude,
               point3d_list[i].latitude,
               point3d_list[i].altitude);
-    
+
     kml_write_xml(-1, "</coordinates>\n");
     kml_write_xml(-1, "</LineString>\n");
     kml_write_xml(-1, "</Placemark>\n");
   }
-  
+
   xfree(point3d_list);
   point3d_list = NULL;
-  
+
   if (!realtime_positioning)  {
     kml_write_xml(-1, "</Folder>\n");
   }
@@ -776,8 +789,8 @@ static void kml_output_tailer(const route_head *header)
 /*
  * Completely different writer for geocaches.
  */    
-static 
-void kml_gc_make_ballonstyle(void) 
+static
+void kml_gc_make_ballonstyle(void)
 {
        // BalloonStyle for Geocaches.
        kml_write_xml(1, "<Style id=\"geocache\"><BalloonStyle><text><![CDATA[\n");
@@ -796,7 +809,7 @@ void kml_gc_make_ballonstyle(void)
        kml_write_xml(-1, "]]></text></BalloonStyle></Style>\n");
 }
 
-static 
+static
 char *
 kml_lookup_gc_icon(const waypoint *waypointp)
 {
@@ -865,7 +878,6 @@ static void kml_geocache_pr(const waypoint *waypointp)
        char *p, *is;
        double lat = waypointp->latitude;
        double lng = waypointp->longitude;
-// optionally "fuzz" lat/lng here.
 
        kml_write_xml(1, "<Placemark>\n");
 
@@ -913,7 +925,7 @@ static void kml_geocache_pr(const waypoint *waypointp)
 
        kml_write_xml(0, "<Data name=\"gc_cont_icon\"><value>%s</value></Data>\n", kml_lookup_gc_container(waypointp));
 
-        // Highlight any issues with the cache, such as temp unavail 
+        // Highlight any issues with the cache, such as temp unavail
         // or archived.
        kml_write_xml(0, "<Data name=\"gc_issues\"><value>");
        if (waypointp->gc_data->is_archived == status_true) {
@@ -958,6 +970,8 @@ static void kml_waypt_pr(const waypoint *waypointp)
                kml_write_xml(-1, "</LookAt>\n");
        }
 #endif
+       waypt_add_to_bounds(&kml_bounds, waypointp);
+       kml_recompute_time_bounds(waypointp);
 
        if (waypointp->gc_data->diff && waypointp->gc_data->terr) {
                kml_geocache_pr(waypointp);
@@ -1014,7 +1028,7 @@ static void kml_waypt_pr(const waypoint *waypointp)
         }
 
        kml_write_xml(0, "<coordinates>%f,%f,%f</coordinates>\n",
-               waypointp->longitude, waypointp->latitude, 
+               waypointp->longitude, waypointp->latitude,
                waypointp->altitude == unknown_alt ? 0.0 : waypointp->altitude);
        kml_write_xml(-1, "</Point>\n");
 
@@ -1025,7 +1039,7 @@ static void kml_waypt_pr(const waypoint *waypointp)
  * TRACKPOINTS
  */
 
-static void kml_track_hdr(const route_head *header) 
+static void kml_track_hdr(const route_head *header)
 {
        computed_trkdata *td;
        track_recompute(header, &td);
@@ -1038,7 +1052,7 @@ static void kml_track_disp(const waypoint *waypointp)
        kml_output_point(waypointp, kmlpt_track);
 }
 
-static void kml_track_tlr(const route_head *header) 
+static void kml_track_tlr(const route_head *header)
 {
        kml_output_tailer(header);
 }
@@ -1047,7 +1061,7 @@ static void kml_track_tlr(const route_head *header)
  * ROUTES
  */
 
-static void kml_route_hdr(const route_head *header) 
+static void kml_route_hdr(const route_head *header)
 {
         kml_output_header(header, NULL);
 }
@@ -1057,11 +1071,61 @@ static void kml_route_disp(const waypoint *waypointp)
         kml_output_point(waypointp, kmlpt_route);
 }
 
-static void kml_route_tlr(const route_head *header) 
+static void kml_route_tlr(const route_head *header)
 {
         kml_output_tailer(header);
 }
 
+// For Earth 5.0, we write a LookAt that encompasses
+// the bounding box of our entire data set and set the event times
+// to include all our data.
+void kml_write_AbstractView(void) {
+  kml_write_xml(1, "<LookAt>\n");
+
+  if (kml_time_min || kml_time_max) {
+    kml_write_xml(1, "<gx:TimeSpan>\n");
+    if (kml_time_min) {
+      char time_string[64];
+      xml_fill_in_time(time_string, kml_time_min, 0, XML_LONG_TIME);
+      if (time_string[0]) {
+        kml_write_xml(0, "<begin>%s</begin>\n", time_string);
+      }
+    }
+    if (kml_time_max) {
+      char time_string[64];
+      xml_fill_in_time(time_string, kml_time_max, 0, XML_LONG_TIME);
+      if (time_string[0]) {
+        kml_write_xml(0, "<end>%s</end>\n", time_string);
+      }
+    }
+    kml_write_xml(-1, "</gx:TimeSpan>\n");
+  }
+
+ // If our BB spans the antemeridian, flip sign on one.
+ // This doesn't make our BB optimal, but it at least prevents us from
+ // zooming to the wrong hemisphere.
+ if (kml_bounds.min_lon * kml_bounds.max_lon < 0) {
+   kml_bounds.min_lon = -kml_bounds.max_lon;
+ }
+
+  kml_write_xml(0, "<longitude>%f</longitude>\n",
+                (kml_bounds.min_lon + kml_bounds.max_lon) / 2);
+  kml_write_xml(0, "<latitude>%f</latitude>\n",
+                (kml_bounds.min_lat + kml_bounds.max_lat) / 2);
+
+  // It turns out the length of the diagonal of the bounding box gives us a
+  // reasonable guess for setting the camera altitude.
+  double bb_size = gcgeodist(kml_bounds.min_lat, kml_bounds.min_lon,
+                             kml_bounds.max_lat, kml_bounds.max_lon);
+  // Clamp bottom zoom level.  Otherwise, a single point zooms to grass.
+  if (bb_size < 1000) {
+    bb_size = 1000;
+  }
+  kml_write_xml(0, "<range>%f</range>\n", bb_size * 1.3);
+
+  kml_write_xml(-1, "</LookAt>\n");
+}
+
 void kml_write(void)
 {
        char import_time[100];
@@ -1077,24 +1141,13 @@ void kml_write(void)
 
        kml_write_xml(0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
 
-       /*
-        * This is a bit cowardly.   Our geocache writer takes advantage
-        * of KML 2.2 features present only in Earth 4.2 and newer.  The
-        * output is actually compatible with Earth versions back to 4.0
-        * for the non-geocaching case, so we'll be conservative and not 
-        * output the new namespace if we don't need it. 
-        */
-       if (geocaches_present) {
-               kml_write_xml(1, kml22_hdr);
-       } else  {
-               kml_write_xml(1, kml21_hdr);
-       }
+       kml_write_xml(1, kml22_hdr);
 
        kml_write_xml(1, "<Document>\n");
 
        now = current_time();
        strftime(import_time, sizeof(import_time), "%c", localtime(&now));
-       if (realtime_positioning) 
+       if (realtime_positioning)
                kml_write_xml(0, "<name>GPS position</name>\n");
        else
                kml_write_xml(0, "<name>GPS device</name>\n");
@@ -1126,7 +1179,7 @@ void kml_write(void)
        }
 
        kml_write_bitmap_style(kmlpt_waypoint, ICON_WPT, NULL);
-        
+
        if (track_waypt_count() || route_waypt_count()) {
                // Style settings for line strings
                kml_write_xml(1, "<Style id=\"lineStyle\">\n");
@@ -1172,12 +1225,13 @@ void kml_write(void)
                        kml_write_xml(1,  "<Folder>\n");
                        kml_write_xml(0,  "<name>Routes</name>\n");
 
-                       route_disp_all(kml_route_hdr, 
+                       route_disp_all(kml_route_hdr,
                                kml_route_tlr, kml_route_disp);
                        kml_write_xml(-1,  "</Folder>\n");
                }
         }
 
+        kml_write_AbstractView();
        kml_write_xml(-1, "</Document>\n");
        kml_write_xml(-1, "</kml>\n");
 }
@@ -1247,12 +1301,12 @@ kml_wr_position(waypoint *wpt)
        {
        waypoint *newest_posn= (waypoint *) QUEUE_LAST(&posn_trk_head->waypoint_list);
        
-       if(radtometers(gcdist(RAD(wpt->latitude), RAD(wpt->longitude), 
+       if(radtometers(gcdist(RAD(wpt->latitude), RAD(wpt->longitude),
                RAD(newest_posn->latitude), RAD(newest_posn->longitude))) > 50) {
                track_add_wpt(posn_trk_head, waypt_dupe(wpt));
        } else {
-               /* If we haven't move more than our threshold, pretend 
-                * we didn't move at  all to prevent Earth from jittering 
+               /* If we haven't move more than our threshold, pretend
+                * we didn't move at  all to prevent Earth from jittering
                 * the zoom levels on us.
                 */
                wpt->latitude = newest_posn->latitude;
@@ -1268,7 +1322,7 @@ kml_wr_position(waypoint *wpt)
         * If we are keeping only a recent subset of the trail, trim the
         * head here.
         */
-       while (max_position_points && 
+       while (max_position_points &&
               (posn_trk_head->rte_waypt_ct >= max_position_points)) {
                waypoint *tonuke = (waypoint *) QUEUE_FIRST(&posn_trk_head->waypoint_list);
                track_del_wpt(posn_trk_head, tonuke);
@@ -1286,7 +1340,7 @@ ff_vecs_t kml_vecs = {
        kml_wr_deinit,
        kml_read,
        kml_write,
-       NULL, 
+       NULL,
        kml_args,
        CET_CHARSET_UTF8, 1,    /* CET-REVIEW */
        { NULL, NULL, NULL, kml_wr_position_init, kml_wr_position, kml_wr_position_deinit }
index 456a26b194c993847ec31b1bc6f237b8aa10e980..9e0b0c7b3b0efbee5122de919670777171a87b14 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<kml xmlns="http://earth.google.com/kml/2.1"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<kml xmlns="http://www.opengis.net/kml/2.2"
+       xmlns:gx="http://www.google.com/kml/ext/2.2">
   <Document>
     <name>GPS device</name>
 <!-- Normal route style -->
         </Placemark>
       </Folder>
     </Folder>
+    <LookAt>
+      <gx:TimeSpan>
+        <begin>2001-06-02T00:18:13Z</begin>
+        <end>2002-05-25T19:05:57Z</end>
+      </gx:TimeSpan>
+      <longitude>-81.356770</longitude>
+      <latitude>36.257086</latitude>
+      <range>2979727.763965</range>
+    </LookAt>
   </Document>
 </kml>
index 8b1d8a2c2f51142e9ad410abef6bcba2468f81a5..e6df651fbf00a38ebf497c317e59119169025b70 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<kml xmlns="http://earth.google.com/kml/2.2"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+<kml xmlns="http://www.opengis.net/kml/2.2"
+       xmlns:gx="http://www.google.com/kml/ext/2.2">
   <Document>
     <name>GPS device</name>
 <!-- Normal waypoint style -->
@@ -155,5 +155,14 @@ Now that it's intuitively obvious to even the most casual observer where the cac
         </Point>
       </Placemark>
     </Folder>
+    <LookAt>
+      <gx:TimeSpan>
+        <begin>2002-08-15T07:00:00Z</begin>
+        <end>2003-06-29T07:00:00Z</end>
+      </gx:TimeSpan>
+      <longitude>-79.930833</longitude>
+      <latitude>41.027500</latitude>
+      <range>2109328.437865</range>
+    </LookAt>
   </Document>
 </kml>